<template>
  <div id="cesiumContainer" style="height: 100%; width: 100%"></div>
  <div
    style="
      position: absolute;
      top: 30px;
      left: 30px;
      height: 200px;
      width: 700px;
    "
  >
    <div class="slider-demo-block">
      <span class="demonstration">纬度剖面</span>
      <el-slider v-model="lon" :min="lonMin" :max="lonMax" :step="0.1" />
    </div>
    <div class="slider-demo-block">
      <span class="demonstration">经度剖面</span>
      <el-slider v-model="lat" :min="latMin" :max="latMax" :step="0.1" />
    </div>
    <div class="slider-demo-block">
      <span class="demonstration">高度剖面</span>
      <el-slider
        v-model="height"
        :min="heightMin"
        :max="heightMax"
        :step="10"
      />
    </div>
  </div>
</template>
<script setup>
import { ref, onMounted, watch } from "vue";
import * as Cesium from "cesium";
import data from "@/assets/output_colors1.json";
import template from "@/assets/output_temperatures3.json";
import * as d3 from "d3";
let lon = ref(0);
let lat = ref(0);
let height = ref(0);
const lonMin = ref(-180);
const lonMax = ref(180);
const latMin = ref(-90);
const latMax = ref(90);
const heightMin = ref(0);
const heightMax = ref(10000); // 假设的最大高度
onMounted(async () => {
  var viewer = new Cesium.Viewer("cesiumContainer", {
    animation: true, //动画小部件
    shouldAnimate: true,
    baseLayerPicker: false, //地图图层组件
    fullscreenButton: false, //全屏组件
    geocoder: false, //地理编码搜索组件
    homeButton: false, //首页组件
    infoBox: false, //信息框
    sceneModePicker: false, //场景模式
    selectionIndicator: false, //选取指示器组件
    timeline: true, //时间轴
    navigationHelpButton: false, //帮助按钮
    navigationInstructionsInitiallyVisible: false,
    // 加载地形函数，提供高分辨率的全球 3D 地形数据
    terrainProvider: await Cesium.createWorldTerrainAsync({
      // 提高地形表面的光照和阴影效果的精度
      requestVertexNormals: true,
      // 水体呈现流动效果
      requestWaterMask: false,
      terrainExaggeration: 3.0, // 设置地形夸张系数
    }),
  });
  viewer.scene.globe.depthTestAgainstTerrain = true; // 确保地形深度检测
  // viewer.scene.primitives.depthTestAgainstTerrain = true; // 确保原始图元深度检测
  // 添加高德影像
  let atLayer = new Cesium.UrlTemplateImageryProvider({
    url: "http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
    minimumLevel: 3,
    maximumLevel: 18,
  });
  viewer.imageryLayers.addImageryProvider(atLayer).name = "gaodeyingxiang";
  // viewer.imageryLayers.addImageryProvider(yinxiang);
  // viewer.imageryLayers.addImageryProvider(yinxiangZJ);
  //   viewer.scene.setTerrainExaggeratio(2.0);
  viewer.cesiumWidget.creditContainer.style.display = "none";
  viewer.timeline.container.style.display = "none"; // 隐藏时间线
  viewer.animation.container.style.visibility = "hidden"; // 隐藏动画控件
  viewer.scene.debugShowFramesPerSecond = true;
  // 扩展Viewer以包含体素检查器混合功能
  // viewer.extend(Cesium.viewerVoxelInspectorMixin);
  window.viewer = viewer;
  console.log(Cesium.VERSION, "cesium版本号");
  ShapeType();
});
// 定义一个多瓦片体素数据提供者的构造函数
function ProceduralMultiTileVoxelProvider(shape) {
  const { min_temperature, max_temperature, temperature_values } = template;
  this.minTemperature = min_temperature;
  this.maxTemperature = max_temperature;
  this.temperatureValues = temperature_values;
  this.shape = shape; // 设置体素形状
  // this.minBounds = Cesium.VoxelShapeType.getMinBounds(shape).clone(); // 设置最小边界
  // this.maxBounds = Cesium.VoxelShapeType.getMaxBounds(shape).clone(); // 设置最大边界
  var minlongitudeRadians = Cesium.Math.toRadians(110);
  var minlatitudeRadians = Cesium.Math.toRadians(15);
  this.minBounds = { x: minlongitudeRadians, y: minlatitudeRadians, z: 1 };
  var maxlongitudeRadians = Cesium.Math.toRadians(124);
  var maxlatitudeRadians = Cesium.Math.toRadians(26);
  this.maxBounds = { x: maxlongitudeRadians, y: maxlatitudeRadians, z: 100000 };
  // this.dimensions = new Cesium.Cartesian3(50, 50, 50); // 设置体素维度
  // 获取最精细层级（原始数据）的长宽高
  this.highestLevelDimensions = new Cesium.Cartesian3(
    temperature_values.length, // x dimension
    temperature_values[0].length, // y dimension
    temperature_values[0][0].length // z dimension
  );
  // 设置层级数目为3
  this._levelCount = 3;
  // 计算最粗略层级的尺寸
  this.dimensions = new Cesium.Cartesian3(
    Math.max(
      Math.ceil(
        this.highestLevelDimensions.x / Math.pow(2, this._levelCount - 1)
      ),
      1
    ),
    Math.max(
      Math.ceil(
        this.highestLevelDimensions.y / Math.pow(2, this._levelCount - 1)
      ),
      1
    ),
    Math.max(
      Math.ceil(
        this.highestLevelDimensions.z / Math.pow(2, this._levelCount - 1)
      ),
      1
    )
  );
  this.paddingBefore = new Cesium.Cartesian3(1, 1, 1); // 设置前填充
  this.paddingAfter = new Cesium.Cartesian3(1, 1, 1); // 设置后填充
  this.names = ["color"]; // 数据通道名称
  this.types = [Cesium.MetadataType.VEC4]; // 数据类型为四维向量
  this.componentTypes = [Cesium.MetadataComponentType.FLOAT32]; // 组件类型为32位浮点数
  this._allVoxelData = new Array(this._levelCount); // 初始化所有层级的体素数据
  // const allVoxelData = this._allVoxelData;
  // const channelCount = Cesium.MetadataType.getComponentCount(this.types[0]);
  // const { dimensions } = this;
  // let data = template;
  // data.temperature_values.forEach((xy) => {
  //   xy.forEach((y) => {
  //     y.forEach((x) => {});
  //   });
  // });
  for (let level = 0; level < this._levelCount; level++) {
    this._initializeLevel(level);
  }
  // for (let level = 0; level < this._levelCount; level++) {
  //   // 遍历每一级
  //   const dimAtLevel = Math.pow(2, level); // 计算该级的维度因子
  //   const voxelCountX = dimensions.x * dimAtLevel; // 计算该级x轴体素数量
  //   const voxelCountY = dimensions.y * dimAtLevel; // 计算该级y轴体素数量
  //   const voxelCountZ = dimensions.z * dimAtLevel; // 计算该级z轴体素数量
  //   const voxelsPerLevel = voxelCountX * voxelCountY * voxelCountZ; // 计算该级体素总数
  //   const levelData = (allVoxelData[level] = new Array(
  //     voxelsPerLevel * channelCount
  //   )); // 初始化该级的体素数据数组

  //   for (let z = 0; z < voxelCountX; z++) {
  //     // 遍历z轴上的每一个体素
  //     for (let y = 0; y < voxelCountY; y++) {
  //       // 遍历y轴上的每一个体素
  //       const indexZY = z * voxelCountY * voxelCountX + y * voxelCountX; // 计算z和y轴的索引
  //       for (let x = 0; x < voxelCountZ; x++) {
  //         // 遍历x轴上的每一个体素
  //         const index = (indexZY + x) * channelCount; // 计算当前体素的颜色数据索引
  //         levelData[index + 0] = x / (voxelCountX - 1); // 设置红色通道值
  //         levelData[index + 1] = y / (voxelCountY - 1); // 设置绿色通道值
  //         levelData[index + 2] = z / (voxelCountZ - 1); // 设置蓝色通道值
  //         levelData[index + 3] = 0.99; // 设置透明度值
  //       }
  //     }
  //   }
  // }
}

ProceduralMultiTileVoxelProvider.prototype._initializeLevel = function (level) {
  // 计算当前层级的尺寸
  const dimAtLevel = Math.pow(2, this._levelCount - level - 1);
  const voxelCountX = Math.floor(this.highestLevelDimensions.x / dimAtLevel);
  const voxelCountY = Math.floor(this.highestLevelDimensions.y / dimAtLevel);
  const voxelCountZ = Math.floor(this.highestLevelDimensions.z / dimAtLevel);

  // 每个体素有两个通道：一个用于颜色和透明度（VEC4），一个用于温度值（SCALAR）
  const channelCountColor = Cesium.MetadataType.getComponentCount(
    Cesium.MetadataType.VEC4
  );
  const channelCountValue = Cesium.MetadataType.getComponentCount(
    Cesium.MetadataType.SCALAR
  );
  const totalChannelCount = channelCountColor;

  const levelData = [];

  // 创建颜色比例尺
  const colorScale = d3
    .scaleSequential(d3.interpolateViridis)
    .domain([this.minTemperature, this.maxTemperature]);

  for (let z = 0; z < voxelCountZ; z++) {
    for (let y = 0; y < voxelCountY; y++) {
      for (let x = 0; x < voxelCountX; x++) {
        let value = this._getDilatedValue(x, y, z, level);
        // console.log(value);
        // 存储温度值到价值通道
        // levelData.push(value);

        // 将颜色和透明度写入颜色通道
        if (value && value !== null) {
          const color = colorScale(value);
          const rgba = d3.color(color);
          levelData.push(
            rgba.r / 255.0,
            rgba.g / 255.0,
            rgba.b / 255.0,
            rgba.opacity !== undefined ? rgba.opacity : 1.0
          );
        } else {
          // 如果没有有效温度值，则设置默认颜色和透明度
          levelData.push(0, 0, 0, 0); // RGB 为 0，Alpha 为 0 表示透明
        }
      }
    }
  }

  // 展平数据
  this._allVoxelData[level] = new Float32Array(levelData);
};

ProceduralMultiTileVoxelProvider.prototype._getDilatedValue = function (
  x,
  y,
  z,
  level
) {
  const dimAtLevel = Math.pow(2, this._levelCount - level - 1);
  const rawX = Math.floor(x * dimAtLevel);
  const rawY = Math.floor(y * dimAtLevel);
  const rawZ = Math.floor(z * dimAtLevel);

  if (
    rawX >= this.highestLevelDimensions.x ||
    rawY >= this.highestLevelDimensions.y ||
    rawZ >= this.highestLevelDimensions.z
  ) {
    return null;
  }

  const value = this.temperatureValues[rawX][rawY][rawZ];
  return value === null ? null : value;
};
// 定义多瓦片体素数据提供者的请求体素数据方法
ProceduralMultiTileVoxelProvider.prototype.requestData = function (options) {
  const { tileLevel, tileX, tileY, tileZ } = options; // 解构赋值获取瓦片等级、位置

  if (tileLevel >= this._levelCount) {
    // 如果请求的瓦片级别超出范围，则返回未定义
    return undefined;
  }
  const type = this.types[0]; // 获取体素数据类型
  const channelCount = Cesium.MetadataType.getComponentCount(type); // 获取每个体素的数据通道数量
  const { dimensions, paddingBefore, paddingAfter } = this; // 获取体素尺寸和填充信息
  const paddedDimensions = Cesium.Cartesian3.fromElements(
    dimensions.x + paddingBefore.x + paddingAfter.x,
    dimensions.y + paddingBefore.y + paddingAfter.y,
    dimensions.z + paddingBefore.z + paddingAfter.z
  ); // 计算带填充的体素尺寸
  const dimAtLevel = Math.pow(2, tileLevel); // 计算该级的维度因子
  const dimensionsGlobal = Cesium.Cartesian3.fromElements(
    dimensions.x * dimAtLevel,
    dimensions.y * dimAtLevel,
    dimensions.z * dimAtLevel
  ); // 计算全局体素尺寸
  const minimumGlobalCoord = Cesium.Cartesian3.ZERO; // 全局坐标的最小值
  const maximumGlobalCoord = new Cesium.Cartesian3(
    dimensionsGlobal.x - 1,
    dimensionsGlobal.y - 1,
    dimensionsGlobal.z - 1
  ); // 全局坐标的最大值
  let coordGlobal = new Cesium.Cartesian3(); // 用于存储转换后的全局坐标

  const dataGlobal = this._allVoxelData; // 获取所有层级的体素数据
  const dataTile = new Float32Array(
    paddedDimensions.x * paddedDimensions.y * paddedDimensions.z * channelCount
  ); // 初始化用于存储瓦片体素数据的数组

  for (let z = 0; z < paddedDimensions.z; z++) {
    // 遍历带填充的z轴上的每一个体素
    const indexZ = z * paddedDimensions.y * paddedDimensions.x;
    for (let y = 0; y < paddedDimensions.y; y++) {
      // 遍历带填充的y轴上的每一个体素
      const indexZY = indexZ + y * paddedDimensions.x;
      for (let x = 0; x < paddedDimensions.x; x++) {
        // 遍历带填充的x轴上的每一个体素
        const indexTile = indexZY + x;

        // 将局部瓦片坐标转换为全局坐标，并限制在全局坐标范围内
        coordGlobal = Cesium.Cartesian3.clamp(
          Cesium.Cartesian3.fromElements(
            tileX * dimensions.x + (x - paddingBefore.x),
            tileY * dimensions.y + (y - paddingBefore.y),
            tileZ * dimensions.z + (z - paddingBefore.z),
            coordGlobal
          ),
          minimumGlobalCoord,
          maximumGlobalCoord,
          coordGlobal
        );

        const indexGlobal =
          coordGlobal.z * dimensionsGlobal.y * dimensionsGlobal.x +
          coordGlobal.y * dimensionsGlobal.x +
          coordGlobal.x;

        for (let c = 0; c < channelCount; c++) {
          dataTile[indexTile * channelCount + c] =
            dataGlobal[tileLevel][indexGlobal * channelCount + c];
        }
      }
    }
  }
  return Promise.resolve([dataTile]); // 返回瓦片体素数据的承诺对象
};

// 定义一个过程生成单个瓦片体素数据的提供者构造函数
function ProceduralSingleTileVoxelProvider(shape) {
  this.shape = shape; // 设置体素形状
  // this.minBounds = Cesium.VoxelShapeType.getMinBounds(shape).clone(); // 设置最小边界
  // this.maxBounds = Cesium.VoxelShapeType.getMaxBounds(shape).clone(); // 设置最大边界
  var minlongitudeRadians = Cesium.Math.toRadians(110);
  var minlatitudeRadians = Cesium.Math.toRadians(15);
  this.minBounds = { x: minlongitudeRadians, y: minlatitudeRadians, z: 1 };
  var maxlongitudeRadians = Cesium.Math.toRadians(124);
  var maxlatitudeRadians = Cesium.Math.toRadians(26);
  this.maxBounds = { x: maxlongitudeRadians, y: maxlatitudeRadians, z: 100000 };
  this.dimensions = new Cesium.Cartesian3(84, 67, 50); // 设置体素维度
  this.names = ["color"]; // 数据通道名称
  this.types = [Cesium.MetadataType.VEC4]; // 数据类型为四维向量
  this.componentTypes = [Cesium.MetadataComponentType.FLOAT32]; // 组件类型为32位浮点数
}

// 定义一个临时颜色变量用于计算颜色值
const scratchColor = new Cesium.Color();

// 定义请求体素数据的方法
ProceduralSingleTileVoxelProvider.prototype.requestData = function (options) {
  if (options.tileLevel >= 1) {
    // 如果请求的瓦片级别大于等于1，则返回未定义（不处理）
    return undefined;
  }

  const dimensions = this.dimensions; // 获取体素尺寸
  const voxelCount = dimensions.x * dimensions.y * dimensions.z; // 计算体素总数
  const type = this.types[0]; // 获取体素数据类型
  const channelCount = Cesium.MetadataType.getComponentCount(type); // 获取每个体素的数据通道数量
  var dataColor = new Float32Array(voxelCount * channelCount); // 创建存储颜色数据的数组

  // const randomSeed = dimensions.y * dimensions.x + dimensions.x; // 生成随机种子
  // Cesium.Math.setRandomNumberSeed(randomSeed); // 设置随机数种子
  // const hue = Cesium.Math.nextRandomNumber(); // 生成色调值

  // for (let z = 0; z < dimensions.z; z++) {
  //   // 遍历z轴上的每一个体素
  //   for (let y = 0; y < dimensions.y; y++) {
  //     // 遍历y轴上的每一个体素
  //     const indexZY = z * dimensions.y * dimensions.x + y * dimensions.x; // 计算z和y轴的索引
  //     for (let x = 0; x < dimensions.x; x++) {
  //       // 遍历x轴上的每一个体素
  //       const lerperX = x / (dimensions.x - 1); // 计算x轴插值
  //       const lerperY = y / (dimensions.y - 1); // 计算y轴插值
  //       const lerperZ = z / (dimensions.z - 1); // 计算z轴插值

  //       // 根据x,y,z坐标计算HSL颜色值
  //       const h = hue + lerperX * 0.5 - lerperY * 0.3 + lerperZ * 0.2;
  //       const s = 1.0 - lerperY * 0.2;
  //       const v = 0.5 + 2.0 * (lerperZ - 0.5) * 0.2;
  //       const color = Cesium.Color.fromHsl(h, s, v, 1.0, scratchColor);

  //       const index = (indexZY + x) * channelCount; // 计算当前体素的颜色数据索引
  //       dataColor[index + 0] = color.red; // 设置红色通道值
  //       dataColor[index + 1] = color.green; // 设置绿色通道值
  //       dataColor[index + 2] = color.blue; // 设置蓝色通道值
  //       dataColor[index + 3] = 0.75; // 设置透明度值
  //     }
  //   }
  // }
  console.log("dataColor", dataColor);
  console.log("data", Float32Array.from(data.color_values));
  dataColor = Float32Array.from(data.color_values);
  return Promise.resolve([dataColor]); // 返回颜色数据的承诺对象
};
// 创建一个体素原语，并将其添加到场景中。
function createPrimitive(provider, customShader, modelMatrix) {
  // 移除所有现有的原语，确保新创建的体素原语是唯一的焦点。
  window.viewer.scene.primitives.removeAll();

  // 添加新的体素原语到场景中，并指定其数据提供者、自定义着色器和模型矩阵。
  const voxelPrimitive = new Cesium.VoxelPrimitive({
    provider: provider, // 体素数据提供者
    customShader: customShader, // 自定义着色器
    modelMatrix: modelMatrix, // 模型变换矩阵
  });
  window.viewer.scene.primitives.add(voxelPrimitive);

  // 将体素原语设置为当前使用的体素检查器中的体素原语。
  // window.viewer.voxelInspector.viewModel.voxelPrimitive = voxelPrimitive;
  // console.log("voxelInspector", window.viewer.voxelInspector);
  // 将相机飞往体素原语的边界球位置，立即完成飞行（持续时间为0秒）。
  window.viewer.camera.flyToBoundingSphere(voxelPrimitive.boundingSphere, {
    duration: 0,
  });

  return voxelPrimitive; // 返回创建的体素原语对象
}

// 定义一个自定义片段着色器，用于基于体素元数据的颜色属性来计算漫反射颜色和透明度。
const customShaderColor = new Cesium.CustomShader({
  fragmentShaderText: `void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)
  {
      material.diffuse = fsInput.metadata.color.rgb;
      float transparency = 1.0 - fsInput.metadata.color.a;
      // material.alpha = 0.3;
      // 模拟光散射效果，使用指数衰减算法
      float thickness = fsInput.voxel.travelDistance * 16.0;
      material.alpha = 1.0 - pow(transparency, thickness);
  }`,
});
// 定义模型变换矩阵，这里是对整个模型进行缩放，使其与WGS84椭球的最大半径相匹配。
const modelMatrix = Cesium.Matrix4.fromScale(
  Cesium.Cartesian3.fromElements(
    Cesium.Ellipsoid.WGS84.maximumRadius,
    Cesium.Ellipsoid.WGS84.maximumRadius,
    Cesium.Ellipsoid.WGS84.maximumRadius
  )
);
let primitive = null;
const ShapeType = () => {
  const provider = new ProceduralMultiTileVoxelProvider(
    Cesium.VoxelShapeType.ELLIPSOID
  );
  // const provider = new ProceduralSingleTileVoxelProvider(
  //   Cesium.VoxelShapeType.ELLIPSOID
  // );
  console.log("provider", provider);
  primitive = createPrimitive(provider, customShaderColor, modelMatrix);
  console.log("primitive", primitive._maxBounds);
  // 获取最大最小边界并转换为地理坐标
  const minBounds = primitive._minBounds;
  const maxBounds = primitive._maxBounds;
  // 转换弧度到度数
  lonMin.value = Cesium.Math.toDegrees(minBounds.y);
  lonMax.value = Cesium.Math.toDegrees(maxBounds.y);
  latMin.value = Cesium.Math.toDegrees(minBounds.x);
  latMax.value = Cesium.Math.toDegrees(maxBounds.x);
  heightMin.value = minBounds.z;
  heightMax.value = maxBounds.z;
  // 初始化滑块的值为范围的中间值
  lon.value = lonMax.value;
  lat.value = latMax.value;
  height.value = heightMax.value;
  const handler = new Cesium.ScreenSpaceEventHandler(
    window.viewer.scene.canvas
  );
  handler.setInputAction(function (movement) {
    const voxelCell = window.viewer.scene.pickVoxel(movement.position);
    if (voxelCell) {
      console.log(voxelCell.getProperty("color"));
    }
  }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
function updateMaxClippingBounds() {
  if (!primitive || !primitive.maxClippingBounds) return;
  // 将经度、纬度、高度转换为笛卡尔坐标
  const cartographic = new Cesium.Cartographic(
    Cesium.Math.toRadians(lon.value),
    Cesium.Math.toRadians(lat.value),
    height.value
  );
  // 更新 maxClippingBounds
  primitive.maxClippingBounds.x = cartographic.latitude;
  primitive.maxClippingBounds.y = cartographic.longitude;
  primitive.maxClippingBounds.z = cartographic.height;
  console.log("maxClippingBounds 已更新:", primitive.maxClippingBounds);
}
// // 创建一个线性归一化函数
// const norm = d3
//   .scaleLinear()
//   .domain([template.min_temperature, template.max_temperature])
//   .range([0, 1]);

// // 创建一个顺序颜色比例尺，模仿 'coolwarm' 色带
// // 使用 d3.interpolateCoolWarm 如果有，否则手动定义颜色渐变
// const coolWarmScale = d3
//   .scaleSequential(
//     // 自定义 'coolwarm' 插值器：从蓝色（#0072B2）到白色（#FFFFFF）再到红色（#D55E00）
//     (t) =>
//       d3.interpolateRgb(
//         "#0072B2",
//         t < 0.5 ? "#FFFFFF" : "#D55E00"
//       )(t < 0.5 ? t * 2 : (t - 0.5) * 2)
//   )
//   .domain([0, 1]);

// // 函数：根据温度值计算颜色
// function temperatureToColor(temperature) {
//   const normalizedValue = norm(temperature);
//   return coolWarmScale(normalizedValue); // 使用顺序比例尺获取颜色
// }

// // 示例：为每个温度值生成颜色
// const colorValues = template.temperature_values.map(temperatureToColor);

// console.log("颜色值:", colorValues);
// 监听滑块值的变化并更新 maxClippingBounds
watch(
  [lon, lat, height],
  () => {
    updateMaxClippingBounds();
  },
  { immediate: true }
); // 立即执行一次以确保初始状态也被处理
</script>
<style scoped>
.read-the-docs {
  color: #888;
}
.slider-demo-block {
  max-width: 600px;
  display: flex;
  align-items: center;
}
.slider-demo-block .el-slider {
  margin-top: 0;
  margin-left: 12px;
}
.slider-demo-block .demonstration {
  font-size: 14px;
  color: var(--el-text-color-secondary);
  line-height: 44px;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-bottom: 0;
}
.slider-demo-block .demonstration + .el-slider {
  flex: 0 0 70%;
}
</style>
